/*-------------------------------------------------------------------------
 *
 * joininfo.c
 *	  joininfo 列表操作例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/optimizer/util/joininfo.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"


/*
 * have_relevant_joinclause
 *		检测是否存在涉及
 *		两个给定关系的连接条件。
 *
 * 注意：连接条件不必仅使用这两个
 * 关系进行评估。这是故意的。例如考虑
 *		SELECT * FROM a, b, c WHERE a.x = (b.y + c.z)
 * 如果a的规模远大于其他表，可能值得交叉连接b和c，
 * 然后在a.x上使用内部索引扫描。因此
 * 我们应该将这个连接条件视为将b连接到c的理由，即使
 * 在该连接步骤中无法应用它。
 */
bool have_relevant_joinclause(PlannerInfo *fc_root,
						 RelOptInfo *fc_rel1, RelOptInfo *fc_rel2)
{
	bool		fc_result = false;
	List	   *fc_joininfo;
	Relids		fc_other_relids;
	ListCell   *fc_l;

	/*
	 * 我们可以扫描任一关系的joininfo列表；最好使用
	 * 较短的那个。
	 */
	if (list_length(fc_rel1->joininfo) <= list_length(fc_rel2->joininfo))
	{
		fc_joininfo = fc_rel1->joininfo;
		fc_other_relids = fc_rel2->relids;
	}
	else
	{
		fc_joininfo = fc_rel2->joininfo;
		fc_other_relids = fc_rel1->relids;
	}

	foreach(fc_l, fc_joininfo)
	{
		RestrictInfo *fc_rinfo = (RestrictInfo *) lfirst(fc_l);

		if (bms_overlap(fc_other_relids, fc_rinfo->required_relids))
		{
			fc_result = true;
			break;
		}
	}

	/*
	 * 我们还需要检查等价类数据结构，该结构可能
	 * 包含未发出的连接信息。
	 */
	if (!fc_result && fc_rel1->has_eclass_joins && fc_rel2->has_eclass_joins)
		fc_result = have_relevant_eclass_joinclause(fc_root, fc_rel1, fc_rel2);

	return fc_result;
}


/*
 * add_join_clause_to_rels
 *	  将'restrictinfo'添加到每个所需关系的joininfo列表中。
 *
 * 注意，restrictinfo节点的相同副本被所有
 * 列表链接。这样可以利用对
 * 限制子句的信息的缓存（但我们必须小心这些信息不依赖于上下文）。
 *
 * 'restrictinfo'描述了连接子句
 * 'join_relids'是参与连接子句的关系列表
 *				 （必须有多个）
 */
void add_join_clause_to_rels(PlannerInfo *fc_root,
						RestrictInfo *fc_restrictinfo,
						Relids fc_join_relids)
{
	int			fc_cur_relid;

	fc_cur_relid = -1;
	while ((fc_cur_relid = bms_next_member(fc_join_relids, fc_cur_relid)) >= 0)
	{
		RelOptInfo *fc_rel = find_base_rel(fc_root, fc_cur_relid);

		fc_rel->joininfo = lappend(fc_rel->joininfo, fc_restrictinfo);
	}
}

/*
 * remove_join_clause_from_rels
 *	  从它所在的所有joininfo列表中删除'restrictinfo'
 *
 * 这反转了add_join_clause_to_rels的效果。它在我们
 * 发现关系根本不需要连接时使用。
 *
 * 'restrictinfo'描述了连接子句
 * 'join_relids'是参与连接子句的关系列表
 *				 （必须有多个）
 */
void remove_join_clause_from_rels(PlannerInfo *fc_root,
							 RestrictInfo *fc_restrictinfo,
							 Relids fc_join_relids)
{
	int			fc_cur_relid;

	fc_cur_relid = -1;
	while ((fc_cur_relid = bms_next_member(fc_join_relids, fc_cur_relid)) >= 0)
	{
		RelOptInfo *fc_rel = find_base_rel(fc_root, fc_cur_relid);

		/*
		 * 将restrictinfo从列表中移除。指针比较
		 * 即可满足要求。
		 */
		Assert(list_member_ptr(fc_rel->joininfo, fc_restrictinfo));
		fc_rel->joininfo = list_delete_ptr(fc_rel->joininfo, fc_restrictinfo);
	}
}
